/******************************************
 * Métodos Numéricos - TP1 "Mandelzoom"
 *
 * bigfloatr.cpp
 * Implementación de BigFloat
 *
 ******************************************/

#include "bigfloat.h"
#include <cmath>
#include <stdlib.h>
#include <stdio.h>
#include <cassert>
#include <sstream>
#include <string>

using namespace std;

    /**
     * Constructor por copia.
     */
    BigFloat::BigFloat(const BigFloat& otro) {
        signo = otro.signo;
        exp = otro.exp;
        precision = otro.precision;

        // pido memoria para el arreglo y lo copio
        mantisa = new short[precision];
        int i;
        for(i=0; i<precision; i++){
            mantisa[i] = otro.mantisa[i];
        }
    }

    /**
     * Operador de asignación que realiza una copia.
	 * requiere que tengan la misma precision
     */
	BigFloat& BigFloat::operator=(const BigFloat& otro){

		signo = otro.signo;
		exp = otro.exp;

		for(int i=0; i<precision; i++){
			mantisa[i] = otro.mantisa[i];
		}

        return (*this);
	}

    /**
     * Construye un BigFloat a partir de un array de char.
     */
    BigFloat::BigFloat(int _precision, char* c) {
        signo=false;
        precision=_precision;
        exp=0;
        mantisa = new short [_precision];
        // RELLENO MANTISA ,EXPONENTE ,SIGNO SEGUN CASOS //


        short p;
        string str(c);
        string aux;
        string::iterator it;
        int index_mantisa=0;


        it=str.begin() ;
        //checkeo el signo si es negativo,lo pongo
        if(*it==45){
            signo=true;
            it++;
        }

        //limpio 0's a izquierda
        while(*it==48){
            it++;
        }
        // recorro hasta encontrar el "."  o hasta terminar el string o la precision
        for (; *it !=46  && it<str.end()&&index_mantisa<_precision; it++ ){
            aux=*it;
            p=atoi(aux.c_str());
            mantisa[index_mantisa]=p;
            index_mantisa++;
        }
        //exp es = index_mantisa ,si hubo digitos enteros , si no igual se mantiene exp=0 ;
        exp =index_mantisa ;
        //si no termino el string salteo el "."
        if(it<str.end()){it++;}

        //si hasta ahora no rellene mantisa con ningun numero , (o sea el numero era 0.xxxx)y no termine string ,limpio 0's a derecha
        //del "."  pero ahora por cada uno decremento el exponente (para la normalizacion)
		if(index_mantisa==0 && it<str.end()){
		    for (; *it==48 && it<str.end(); it++ ){
                exp --;
            }
        }
        //continuo rellenando la mantisa
        for (; it < str.end()&&index_mantisa<_precision; it++ ){
            aux=*it;
            p=atoi(aux.c_str());
            mantisa[index_mantisa]=p;
            cout << p;
            index_mantisa++;
        }
        //si todo me dio 0 , quiero el exp en 0 por prolijidad
       if(index_mantisa == 0){exp = 0;}
        //relleno la mantisa que queda con 0
        while (index_mantisa<_precision){
            mantisa[index_mantisa]=0;
            index_mantisa++;
        }


    }

    /**
     * Construye un BigFloat a partir de un double
     * Suponemos una precision maxima de 6 digitos (si se quiere mas cambiar char format[10])
     */
    BigFloat::BigFloat(int _precision, double d) {
         precision=_precision;
         signo=false ;
         exp=0;
         mantisa= new short[_precision];


        // PASAJE DOUBLE d A CHAR[] chr_double//
        char format[10];
        sprintf(format,"%%.%dlf",_precision+1);


        int size_arrchar=_precision+3;
        char chr_double[size_arrchar];
        snprintf(chr_double,size_arrchar, format, d);

        // FIN  PASAJE DOUBLE d A CHAR[] chr_double //




        // RELLENO MANTISA ,EXPONENTE SEGUN CASOS //
       // cout<<"chr_double : "<<chr_double <<endl;

        short p;
        string str_double(chr_double);
        //cout<<"str_double : "<<str_double <<endl;



        string aux;
        string::iterator it;
        int index_mantisa=0;

        it=str_double.begin() ;
        //checkeo el signo
        if(*it==45){
            signo=true;
            it++;
        }
        //limpio 0's a izquierda


        while(*it==48){
            it++;
        }

        // recorro hasta encontrar el "."  o hasta terminar el string o la precision
        for (; *it !=46  && it<str_double.end() && index_mantisa<_precision; it++ ){
            aux=*it;
            p=atoi(aux.c_str());
            mantisa[index_mantisa]=p;
            index_mantisa++;
        }



        //exp es = index_mantisa ,si hubo digitos enteros , si no igual se mantiene exp=0 ;
        exp =index_mantisa ;
        //si no termino el string salteo el "."
        if(it<str_double.end()){it++;}


        //si hasta ahora no rellene mantisa con ningun numero , (o sea el numero era 0.xxxx) y no termine el string, limpio 0's a derecha
        //del "."  ademas por cada uno decremento el exponente (para la normalizacion)
        if(index_mantisa==0 && it<str_double.end()){
            for(; *it==48 && it<str_double.end(); it++ ){
                exp --;
            }
        }

        //continuo rellenando la mantisa
        for (; it < str_double.end() && index_mantisa<_precision; it++ ){
            aux=*it;
            p=atoi(aux.c_str());
            mantisa[index_mantisa]=p;
            index_mantisa++;
        }
        //si todo me dio 0 , quiero el exp en 0 por prolijidad
        if(index_mantisa == 0){exp = 0;}
        //si me quedo mantisa por rellenar,relleno con 0
        while (index_mantisa<_precision){
            mantisa[index_mantisa]=0;
            index_mantisa++;
        }




    }

    /**
     * Construye un BigFloat a partir de sus campos:
     * precision, signo, mantisa y exponente.
     * El array que contiene la mantisa se copia.
     */
    BigFloat::BigFloat(int _precision, bool _signo, short _mantisa[], int exponente) {
        signo = _signo;
        exp = exponente;
        precision = _precision;
        bool esCero = true;
       	// pido memoria para el arreglo y lo copio
        mantisa = new short[precision];
        int i;
        //int shift = 0;
        // calculo los shifts necesarios para normalizar la mantisa
        for(i=0; (i<precision) && (_mantisa[i]==0); i++){
            exp--;
        }
        int j;
        for(j=0; i<precision; j++){
            mantisa[j] = _mantisa[i];
            esCero = esCero && mantisa[j]==0;
            i++;
        }
        while(j<precision){
            mantisa[j] = 0;
            j++;
        }
        if(esCero){exp=0;}

    }


    /**
     * Destructor de BigFloat.
     */
    BigFloat::~BigFloat(){
        delete[] mantisa;
    }


    /**
     * Suma a este BigFloat otro. Requiere que tengan igual precision. Retorna this.
     */
    BigFloat& BigFloat::sumar(const BigFloat& n) {
        sumaYResta(n, 0);
        return *this;
    }

    /**
     * Resta de este BigFloat otro. Requiere que tengan igual precisión. Retorna this.
     */
    BigFloat& BigFloat::restar(const BigFloat& n) {
        sumaYResta(n, 1);
	    return *this;
    }

    /**
     * Realiza la suma y la resta de BigFloats dependiendo de un flag resta (si es 0 suma, sino resta).
     */
    void BigFloat::sumaYResta(const BigFloat& n, const bool resta) {

        int i, sumaExps;
        short aux1[precision];
        short aux2[precision];
        bool esCero = true;

        // En los dos casos que siguen se denormaliza al BigFloat de forma tal que
        // los dos operandos tengan el mismo exponente,para luego poder sumar las mantisas.
        // Notar que si la diferencia de exponentes es más grande que la precisión el resultado
        // de la suma va a ser el operando con mayor exponente.
        if (exp < n.exp){
            for(i=0; i < precision; i++){
                if (i<(n.exp - exp)){aux1[i] = 0;}
                else{aux1[i] = mantisa[i-n.exp+exp];}
                aux2[i] = n.mantisa[i];
            }
            // el exponente de resultado en un principio es el mas grande
            sumaExps = n.exp;
        }
        if (exp > n.exp){
            for(i=0; i < precision; i++){
                if (i<(exp - n.exp)){aux2[i] = 0;}
                else{aux2[i] = n.mantisa[i-exp+n.exp];}
                aux1[i] = mantisa[i];
            }
            // el exponente de resultado en un principio es el mas grande
            sumaExps = exp;
        }
        if (exp == n.exp){
            for(i=0; i < precision; i++){
                aux1[i] = mantisa[i];
                aux2[i] = n.mantisa[i];
            }
            // el exponente del resultado es el mas grande, en este caso cualquiera de los dos porque son iguales
            sumaExps = exp;
        }

        bool signo2 = n.signo;
        //si es una resta niego el signo del segundo operando
        if(resta){signo2 = !signo2;}

        // si los dos tienen el mismo signo es una suma y luego se conserva el signo
        if (signo==signo2){
            short carry = 0;
            short arr[precision];

            // se suman las mantisas elemento a elemento teniendo en cuenta el accarreo
            for(i=precision-1; i>=0; i--){
                arr[i] = (aux1[i] + aux2[i] + carry) % 10;
                carry = (aux1[i] + aux2[i] + carry) / 10;
            }
            // Si en el dígito más significativo hubo acarreo se debe "correr la coma"
            // es decir sumarle uno al exponente del resultado para mantener el formato 0,mantisa
    		// Notar que se pierde el dígito menos significativo en este caso
            if (carry == 1){
                for(i=0; i < precision-1; i++){
                    mantisa[i+1] = arr[i];
                }
                mantisa[0] = 1;
                // ver que el exp no sea el maximo
                sumaExps++;
                esCero = false;
            }
            else{
                for(i=0; i < precision; i++){
                    mantisa[i] = arr[i];
                    esCero = esCero && mantisa[i]==0;
                }
            }
            // el signo es el mismo que antes de sumar
        }
        // en este caso hay que restarlos
        else{
            short carry = 0;
            short arr[precision];
            // ordenada describe si se está restando al número mayor el menor
            // Notar que si son iguales, entonces ordenada es true
            bool ordenada;
            // hay que ver cuál operando es el negativo para poder saber si está ordenada
            if (signo){ordenada = aux1[0] < aux2[0];}
            else{ordenada = aux1[0] > aux2[0];}
            for(i=0; (i<precision-1) && (aux1[i]==aux2[i]); i++){
                if (signo){ordenada = aux1[i+1] <= aux2[i+1];}
                else{ordenada = aux1[i+1] >= aux2[i+1];}
            }
            // se restan las mantisas elemento a elemento teniendo en cuenta el accarreo
            // si no está "ordenada" la resta se los resta al revés y luego se cambia el signo

            for(i=precision-1; i>=0; i--){
                if (ordenada){
                    arr[i] = (signo?-aux1[i]:aux1[i]) + (signo2?-aux2[i]:aux2[i]) - carry;
                }
                else{
                    arr[i] = (!signo?-aux1[i]:aux1[i]) + (!signo2?-aux2[i]:aux2[i]) - carry;
                }
                carry = ((arr[i]<0)?1:0);
                if ((arr[i]<0) && (i != 0)){arr[i] = arr[i] + 10;}
            }

            //shift cuenta la cantidad de shift que hay que hacer para normalizar el resultado
            // es decir que va a indicar donde estÃ¡ el primer dÃ­gito distinto de 0
            int shift;
            for(shift=0; (shift<precision) && (arr[shift] == 0); shift++){}
            // Hay que tener en cuenta los shifts para normalizar la mantisa y sumarle
            // al exponente lo que sea necesario.
            for(i=0; i < precision-shift ; i++){
                mantisa[i] = ((arr[i+shift]<0)?-arr[i+shift]:arr[i+shift]);
                esCero = esCero && mantisa[i]==0;
            }
            while(i<precision){
                mantisa[i] = 0;
                i++;
            }
            // El exponente del resultado es el mismo que el de los sumandos menos los shift
            // hay que verificar que quede en el rango vÃ¡lido para un int
            sumaExps = sumaExps - shift;

            signo = !ordenada;
        }

        if(esCero){exp = 0;}
        else{exp = sumaExps;}
    }

    /**
     * Multiplica este BigFloat por otro. Retorna this.
     */
    BigFloat& BigFloat::multiplicar(const BigFloat& n) {

        bool _signo = (signo!=n.signo);
        int _exp = exp + n.exp;
        short* _mantisa = new short[precision];
        bool esCero = true;

        int i;
        short* aux = new short[2*precision];
        for(i=0;i<2*precision;i++){
            aux[i]=0;
        }

        int j;
        int temp;
        for(i=precision-1;i>=0;i--){
        //aca aux tiene el doble de elem +en el array recordar
            for(j=precision-1; j>=0;j--){
                temp = aux[i+j+1] + (n.mantisa[i]*mantisa[j]);
                aux[i+j+1] = temp%10;
                aux[i+j] += temp/10;
            }
        }

        //Asigno _mantisa segun haya habido o no carry en el digito mas significativo del resultado
        int pos;
        //Si hubo carry , voy a asignar la mantisa desde el aux[0]
        if(aux[0]!=0){
            pos=0;
        }
        //Si no hubo carry , voy a asignarla desde aux[1] y ademas el exponente decrece en uno
        else{
            _exp--;
            pos=1;
        }

        for(i=0;i<precision ;i++){
            _mantisa[i]=aux[pos+i];
            esCero = esCero && _mantisa[i]==0;
        }
        delete[] aux;

        signo = _signo;
        short* aBorrar = mantisa;
        delete[] aBorrar;
        mantisa = _mantisa;
        if(esCero){exp = 0;}
        else{exp = _exp;}

        return *this;
    }

    /**
     * Multiplica este BigFloat por sí mismo. Retorna this.
     */
    BigFloat& BigFloat::cuadrar() {
        return this->multiplicar(*this);
    }

	/**
	 * Indica si el BigFloat es mayor que cero.
	 */
	bool BigFloat::positivo() const {
	    bool res = !signo;
	    bool mant=false ;
        int i;
        if(res){
            for(i=0; i<precision; i++){
                mant = mant || (mantisa[i]!=0);
            }
               return mant  ;
        }
        return res ;
	}

	/**
	 * Indica si este BigFloat es mayor que el pasado por parámetro.
	 */
	bool BigFloat::mayor(const BigFloat& que) const {
	    BigFloat aux(*this);
            aux.restar(que);
            return aux.positivo();
	}

	/**
	 * Calcula la división entre dos BigFloat's, sujeto a las siguientes
	 * condiciones:
	 *      - ambos parámetros son positivos (de no ser así los toma como si lo fueran);
	 *      - num < 10;
	 *      - el resultado estará entre 40 y 4000. (luego div < 1/4);
	 */
	int BigFloat::razon(const BigFloat& num, const BigFloat& div) {

        // digitos es la cantidad de digitos que va a tener el int que devuelva
        int digitos = num.exp - div.exp;
        if (digitos > num.precision){digitos = num.precision;}
        if (digitos > 9){digitos = 9;}
        int dividendo = num.mantisaToInt(digitos);
        int divisor = div.mantisaToInt(digitos);

        double power = pow((double)10, digitos);
        int res = (dividendo * power) / divisor;

        return res;
	}


	/**
	 * Muestra el BigFloat en un stream de salida.
	 */
    ostream& BigFloat::mostrar(ostream& out) const {
        int i;
        //out << "\nPrecision = " << precision << endl;
        out << (signo? "-":"+") << "0,";
        for(i=0; i<precision; i++){
            out << mantisa[i];
        }
        out << " x 10e" << exp;
        return out;
    }

    /**
     * Compara el BigFloat con un string, si son distintos termina la ejecución del programa.
     * Es de utilidad para el testeo.
     */
    void BigFloat::asegurar(const string s) const {
        ostringstream out;
        mostrar(out);
        string t = out.str();
        if (s != t) {
            cerr << "ERROR" << endl;
            cerr << s << " != " << t << endl;
            cerr << "El primero es lo esperado y el segundo lo obtenido." << endl;
            assert(false);
        }
    }

    /**
     * Transforma el número representado por los primeros largo digitos de la mantisa a un int.
     * Requiere: largo <= precision
     */
    int BigFloat::mantisaToInt(const int largo) const{
	    int i;
	    int res = 0;
	    for(i=largo-1; i>=0; i--){
            double power = pow((double)10, largo -1 -i);
            res = res + mantisa[i] * power;
	    }
	    return res;
    }

/**
 * Sobrecarga de operator<< que muestra el BigFloat en un stream.
 */
ostream& operator<<(ostream& out, const BigFloat& n) {
    return n.mostrar(out);
}
